package cn.jly.spring.boot.cloud.controller;

import cn.jly.spring.boot.cloud.beans.User;
import cn.jly.spring.boot.cloud.dao.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.context.request.async.WebAsyncTask;

import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

/**
 * 可以先释放容器分配给请求的线程与相关资源，减轻系统负担，释放了容器所分配线程的请求，
 * 其响应将被延后，可以在耗时处理完成（例如长时间的运算）时再对客户端进行响应。
 * 一句话：增加了服务器对客户端请求的吞吐量（实际生产上我们用的比较少，如果并发请求量很大的情况下，
 * 我们会通过nginx把请求负载到集群服务的各个节点上来分摊请求压力，当然还可以通过消息队列来做请求的缓冲）。
 *
 * @author lanyangji
 * @description 异步请求测试
 * @date 2020/12/5 上午 10:50
 */
@Controller
public class AsyncController {

    @Autowired
    UserRepository userRepository;

    @Autowired
    ThreadPoolTaskExecutor threadPoolTaskExecutor;

    /**
     * 也是spring支持的方式，可以处理更复杂的场景
     * <p>
     * DeferredResult可以处理一些相对复杂一些的业务逻辑，最主要还是可以在另一个线程里面进行业务处理及返回，
     * 即可在两个完全不相干的线程间的通信。
     *
     * @param username
     * @return
     */
    @ResponseBody
    @GetMapping("/deferredResultReq")
    public DeferredResult<List<User>> deferredResultReq(String username) {
        System.out.println("外部线程 -> " + Thread.currentThread().getName());
        // 设置超时时间
        final DeferredResult<List<User>> deferredResult = new DeferredResult<>(30 * 1000L);
        // 超时处理，采用委托机制
        deferredResult.onTimeout(new Runnable() {
            @Override
            public void run() {
                System.out.println("超时 -> " + Thread.currentThread().getName());
                deferredResult.setResult(Collections.emptyList());
            }
        });
        // 正常业务逻辑处理完成
        deferredResult.onCompletion(new Runnable() {
            @Override
            public void run() {
                System.out.println("调用完成 ->" + Thread.currentThread().getName());
                final Object result = deferredResult.getResult();
                System.out.println("正常返回结果 -> " + result);
            }
        });
        // 处理业务逻辑
        threadPoolTaskExecutor.execute(new Runnable() {
            @Override
            public void run() {
                // 处理业务逻辑
                final List<User> users = userRepository.getFirst3ByUsernameLike("%" + username + "%");
                // 封装返回结果
                deferredResult.setResult(users);
            }
        });

        return deferredResult;
    }

    /**
     * spring支持的方式
     *
     * @param username
     * @return
     */
    @ResponseBody
    @GetMapping("/webAsyncReq")
    public WebAsyncTask<List<User>> webAsyncReq(String username) {
        System.out.println("外部线程 -> " + Thread.currentThread().getName());

        // 正常异步处理逻辑
        final WebAsyncTask<List<User>> webAsyncTask = new WebAsyncTask<>(30 * 1000, new Callable<List<User>>() {
            @Override
            public List<User> call() throws Exception {
                System.out.println("内部线程 -> " + Thread.currentThread().getName());
                TimeUnit.SECONDS.sleep(4);
                return userRepository.getFirst3ByUsernameLike(username);
            }
        });
        // 超时处理逻辑
        webAsyncTask.onTimeout(new Callable<List<User>>() {
            @Override
            public List<User> call() throws Exception {
                System.out.println("超时");
                return Collections.emptyList();
            }
        });

        return webAsyncTask;
    }

    /**
     * callable方式
     *
     * @param username
     * @return
     */
    @ResponseBody
    @GetMapping("/callableReq")
    public Callable<List<User>> callableReq(String username) {
        System.out.println("外部线程 -> " + Thread.currentThread().getName());

        return new Callable<List<User>>() {
            @Override
            public List<User> call() throws Exception {
                Thread.sleep(3000);
                System.out.println("内部线程名 -> " + Thread.currentThread().getName());

                return userRepository.getFirst3ByUsernameLike("%" + username + "%");
            }
        };
    }

    /**
     * 基于servlet实现异步请求
     *
     * @param request
     * @param response
     */
    @GetMapping("/servletAsyncReq")
    public void servletAsyncRequest(HttpServletRequest request, HttpServletResponse response) {
        final AsyncContext asyncContext = request.startAsync();
        //设置监听器:可设置其开始、完成、异常、超时等事件的回调处理
        asyncContext.addListener(new AsyncListener() {
            @Override
            public void onComplete(AsyncEvent event) throws IOException {
                System.out.println("执行完成");
            }

            @Override
            public void onTimeout(AsyncEvent event) throws IOException {
                System.out.println("超时");
            }

            @Override
            public void onError(AsyncEvent event) throws IOException {
                System.out.println("发生错误 -> " + event.getThrowable().getMessage());
            }

            @Override
            public void onStartAsync(AsyncEvent event) throws IOException {
                System.out.println("线程开始");
            }
        });
        // 设置超时时间
        asyncContext.setTimeout(20000);
        asyncContext.start(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(300);
                    System.out.println("内部线程名 -> " + Thread.currentThread().getName());

                    // 响应
                    asyncContext.getResponse().setCharacterEncoding(StandardCharsets.UTF_8.name());
                    asyncContext.getResponse().setContentType("text/html;charset=UTF-8");
                    asyncContext.getResponse().getWriter().write("这是异步返回");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }

                //异步请求完成通知
                //此时整个请求才完成
                asyncContext.complete();
            }
        });
        //此时之类 request的线程连接已经释放了
        System.out.println("主线程 -> " + Thread.currentThread().getName());
    }
}
